#include "esp_log.h"
#include "nvs_flash.h"
/* BLE */
#include "console/console.h"
#include "esp_mac.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "services/gap/ble_svc_gap.h"

static const char *tag = "ble_adv";

static uint8_t own_addr_type = BLE_OWN_ADDR_PUBLIC;
uint8_t base_mac[6];
void print_addr(const void *addr)
{
	const uint8_t *u8p;

	u8p = addr;
	ESP_LOGI(tag,
			 "%02x:%02x:%02x:%02x:%02x:%02x",
			 u8p[5],
			 u8p[4],
			 u8p[3],
			 u8p[2],
			 u8p[1],
			 u8p[0]);
}
void ble_store_config_init(void);

/**
 * Enables advertising with the following parameters:
 *     o General discoverable mode.
 *     o Undirected connectable mode.
 */
static void bleprph_advertise(void)
{
	struct ble_gap_adv_params adv_params;
	struct ble_hs_adv_fields fields;
	const char *name;
	int rc;

	/**
	 *  Set the advertisement data included in our advertisements:
	 *     o Flags (indicates advertisement type and other general info).
	 *     o Advertising tx power.
	 *     o Device name.
	 *     o 16-bit service UUIDs (alert notifications).
	 */

	memset(&fields, 0, sizeof fields);

	/* Advertise two flags:
	 *     o Discoverability in forthcoming advertisement (general)
	 *     o BLE-only (BR/EDR unsupported).
	 */
	fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;

	/* Indicate that the TX power level field should be included; have the
	 * stack fill this value automatically.  This is done by assigning the
	 * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
	 */
	fields.tx_pwr_lvl_is_present = 1;
	fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;

	name = ble_svc_gap_device_name();
	fields.name = (uint8_t *)name;
	fields.name_len = strlen(name);
	fields.name_is_complete = 1;

	rc = ble_gap_adv_set_fields(&fields);
	if (rc != 0) {
		ESP_LOGE(tag, "error setting advertisement data; rc=%d\n", rc);
		return;
	}

	/* Begin advertising. */
	memset(&adv_params, 0, sizeof adv_params);
	// 不可连接的非定向广播 Undirected Non-Connectable
	adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
	// 通用可发现模式（General Discoverable Mode）
	adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
	rc = ble_gap_adv_start(
		own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, NULL, NULL);
	if (rc != 0) {
		ESP_LOGE(tag, "error enabling advertisement; rc=%d\n", rc);
		return;
	}
}

static void bleprph_on_reset(int reason)
{
	ESP_LOGE(tag, "Resetting state; reason=%d\n", reason);
}

static void bleprph_on_sync(void)
{
	int rc;

	/* Make sure we have proper identity address set (public preferred) */
	rc = ble_hs_util_ensure_addr(0);

	assert(rc == 0);

	/* Figure out address to use while advertising (no privacy for now) */
	rc = ble_hs_id_infer_auto(0, &own_addr_type);
	if (rc != 0) {
		ESP_LOGE(tag, "error determining address type; rc=%d\n", rc);
		return;
	}

	/* Printing ADDR */
	uint8_t addr_val[6] = {0};
	rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);

	ESP_LOGI(tag, "BLE Device Address: ");
	print_addr(addr_val);
	ESP_LOGI(tag, "\n");
	/* Begin advertising. */

	bleprph_advertise();
}

void bleprph_host_task(void *param)
{
	ESP_LOGI(tag, "BLE Host Task Started");
	/* This function will return only when nimble_port_stop() is executed */
	nimble_port_run();

	nimble_port_freertos_deinit();
}

void app_main(void)
{
	int rc;

	/* Initialize NVS — it is used to store PHY calibration data */
	esp_err_t ret = nvs_flash_init();
	if (ret == ESP_ERR_NVS_NO_FREE_PAGES
		|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
		ESP_ERROR_CHECK(nvs_flash_erase());
		ret = nvs_flash_init();
	}
	ESP_ERROR_CHECK(ret);

	ret = esp_base_mac_addr_get(base_mac);
	if (ret == ESP_OK) {
		ESP_LOGI(tag, "get base mac success");
		print_addr(base_mac);
	} else {
		ESP_LOGE(tag, "get base mac failed %d", ret);
	}

	ret = nimble_port_init();
	if (ret != ESP_OK) {
		ESP_LOGE(tag, "Failed to init nimble %d ", ret);
		return;
	}
	/* Initialize the NimBLE host configuration. */
	ble_hs_cfg.reset_cb = bleprph_on_reset;
	ble_hs_cfg.sync_cb = bleprph_on_sync;
	ble_hs_cfg.gatts_register_cb = NULL;
	ble_hs_cfg.store_status_cb = ble_store_util_status_rr;

	ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
	ble_hs_cfg.sm_sc = 0;

	/* Set the default device name. */
	rc = ble_svc_gap_device_name_set("elegrp-smart");
	assert(rc == 0);

	/* XXX Need to have template for store */
	ble_store_config_init();

	nimble_port_freertos_init(bleprph_host_task);
}
